#include "stdafx.h"
#include <boost/test/auto_unit_test.hpp>
#include <boost/test/execution_monitor.hpp>
#include "bmock.hpp"
#include "bmock_control_tools_mock.hpp"

struct custom_type2 {
	int i;
};

namespace std {
	istream &operator>>(istream &is,custom_type2 &ct) {
		is >> ct.i;
		return is;
	}
	ostream &operator<<(ostream &os,const custom_type2 &ct) {
		os << ct.i;
		return os;
	}
}

static int f() {
	MOCK(0,());
	return 1;
}
static const char* const h() {
	MOCK(0,());
	return NULL;
}
static custom_type2 i1() {
	MOCK(0,());
	return custom_type2();
}
static const custom_type2& i2() {
	MOCK(0,());
	return *(new custom_type2());
}
static custom_type2& i3() {
	MOCK(0,());
	return *new custom_type2();	
}

struct f_tester2 {
	static const int expected_ = 10;
	f_tester2()
		:result_(0)
	{
		BMOCK_CREATE_MOCK(*);
		BMOCK_EXPECT_RETURN(expected_,f());
		BMOCK_REPLAY;
	}
	~f_tester2() {
		BMOCK_VERIFY;
	}
	int result_;
};

const int f_tester2::expected_;


BMOCK_TEST(f_tester2,test_ReturnValue) {
	BOOST_CHECK_NO_THROW(result_ = f());
	BOOST_CHECK_EQUAL(expected_,result_);
}

BOOST_AUTO_TEST_CASE(testReturnValueControl2) {
	const int expected = 10;
	int actual;
	__BMOCK_CONTROL(c);
	__BMOCK_CREATE_MOCK_C((&c),*);
	BOOST_CHECK_NO_THROW(__BMOCK_EXPECT_RETURN_C(c,expected,f()));
	__BMOCK_REPLAY_C(c);
	BOOST_CHECK_NO_THROW(actual = f());
	__BMOCK_VERIFY_C(c);
	BOOST_CHECK_EQUAL(expected,actual);
}

BOOST_AUTO_TEST_CASE(testReturnPtrControl2) {
	const char *const expected = "abcdefg";
	const char *actual = NULL;;
	__BMOCK_CONTROL(c);
	__BMOCK_CREATE_MOCK_C((&c),*);
	BOOST_CHECK_NO_THROW(__BMOCK_EXPECT_RETURN_C(c,expected,h()));
	__BMOCK_REPLAY_C(c);
	BOOST_CHECK_NO_THROW(actual = h());
	__BMOCK_VERIFY_C(c);
	BOOST_CHECK_EQUAL(expected,actual);
}

BOOST_AUTO_TEST_CASE(testExpectReturnRequired2) {
	__BMOCK_CONTROL(c);
	const bmock::control_tools_ptr t(new bmock::control_tools_mock(c));
	const bmock::location          loc(__FILE__,__LINE__+6);

	__BMOCK_EXPECT_THROW_C(c,new bmock::location(loc),t->error(bmock::message(bmock::CHECK_RETURN_REQUIRED),loc));
	__BMOCK_REPLAY_C(c);
	{
		bmock::test_mode_control c1(bmock::location(__FILE__,__LINE__),t);__BMOCK_CREATE_MOCK_C((&c1),*);
		BOOST_CHECK_THROW(__BMOCK_EXPECT_C(c1,f()),bmock::location);
	}
	__BMOCK_VERIFY_C(c);
}

BOOST_AUTO_TEST_CASE(testInvalidReturnValueType2) {
	__BMOCK_CONTROL(c);
	const bmock::control_tools_ptr t(new bmock::control_tools_mock(c));
	const bmock::location          loc(__FILE__,__LINE__+7);

	__BMOCK_EXPECT_THROW_C(c,new bmock::location(loc),t->error(bmock::message(bmock::BAD_RETURN_TYPE),loc));
	__BMOCK_REPLAY_C(c);
	{
		const char* c = "wrong";
		bmock::test_mode_control c1(bmock::location(__FILE__,__LINE__),t);__BMOCK_CREATE_MOCK_C((&c1),*);
		BOOST_CHECK_THROW(__BMOCK_EXPECT_RETURN_C(c1,c,f()),bmock::location);
	}
	__BMOCK_VERIFY_C(c);
}

struct i_tester2 {
	static const int exp_i = 129;
	i_tester2() {
		BMOCK_CREATE_MOCK(*);
		expected_.i = exp_i;

	}
	~i_tester2() {
		BMOCK_VERIFY;
	}
	custom_type2 expected_;
};

BMOCK_TEST(i_tester2,test_ReturnCustomType) {
	custom_type2 result = {0};
	BMOCK_EXPECT_RETURN(expected_,i1());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(result = i1());
	BOOST_CHECK_EQUAL(expected_.i,result.i);
}

BMOCK_TEST(i_tester2,test_ReturnCustomTypeConstRef) {
	BMOCK_EXPECT_RETURN(expected_,i2());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(
		const custom_type2 &result = i2();
	BOOST_CHECK_EQUAL(expected_.i,result.i);
	BOOST_CHECK_EQUAL(&expected_, &result);
	);
}

BMOCK_TEST(i_tester2,test_ReturnCustomTypeRef) {
	BMOCK_EXPECT_RETURN(expected_,i3());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(
		custom_type2 &result = i3();
	BOOST_CHECK_EQUAL(expected_.i,result.i);
	BOOST_CHECK_EQUAL(&expected_, &result);
	);
}

static std::string i4() {
	MOCK(0,());
	return std::string();
}

BMOCK_TEST(i_tester2,test_ReturnString) {
	const std::string expected = "EXPECTED";
	BMOCK_EXPECT_RETURN(expected,i4());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(
		std::string result = i4();
	BOOST_CHECK_EQUAL(expected,result);
	);
}

struct tester2{
	tester2() {
        BMOCK_CREATE_MOCK(*);
	}
};

typedef void(*pt2Func)(int);
static void tmpFunc(int) {}

static pt2Func k() {
	MOCK(0,());
	return NULL;
}


BMOCK_TEST(tester2, test_return_value_function_pointer) {
	pt2Func p;

	BMOCK_EXPECT_RETURN(tmpFunc, k());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(p = k());
	BOOST_CHECK_EQUAL(p, tmpFunc);
	BMOCK_VERIFY;
}

struct fund_tester2 {
	fund_tester2():ret_val(5) {
		BMOCK_CREATE_MOCK(*);
	}	
	const int ret_val;
	double casted_ret_val;
private:
	fund_tester2& operator=(const fund_tester2&) {return *this;} //avoid warning
};

static double g1() {
	MOCK(0,());
	return 1;
}

BMOCK_TEST(fund_tester2, test_converting_fundamental_type) {
	BMOCK_EXPECT_RETURN(ret_val, g1());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(casted_ret_val = g1());
	BOOST_CHECK_EQUAL(casted_ret_val, double(ret_val));
	BMOCK_VERIFY;

}

struct casted_from2 {
	casted_from2():x_int(5){}
	int x_int;
};
struct casted_to2 {
	casted_to2(){}
	casted_to2(const casted_from2& c):x_double(c.x_int) {}
	double x_double;
};

static casted_to2 g2() {
	MOCK(0,());
	return casted_to2();
}

BMOCK_TEST(tester2, test_converting_structs) {
	const casted_from2 c;
	casted_to2 d;

	BMOCK_EXPECT_RETURN(c,g2());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(d = g2());
	BOOST_CHECK_EQUAL(d.x_double, double(c.x_int));	
	BMOCK_VERIFY;
}


struct base2 {
	base2(int xx=0):x(xx) {}
	int x;
};

struct derived2 : public base2 {
	derived2(int xx=0):base2(xx),y(0){}
	int y;
};

static base2 g3() {
	MOCK(0,());
	return base2();
}

BMOCK_TEST(tester2, test_coverting_base_derived) {
	base2 b;
	const derived2 d(4);

	BMOCK_EXPECT_RETURN(d,g3());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(b = g3());
	BOOST_CHECK_EQUAL(b.x, d.x);
	BMOCK_VERIFY;
}

static base2* g4() {
	MOCK(0,());
	return NULL;
}

BMOCK_TEST(tester2, test_converting_base_derived_pointers) {
	base2* pb = NULL;
	derived2* pd = new derived2(4);

	BMOCK_EXPECT_RETURN(pd,g4());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(pb = g4());
	BOOST_CHECK_EQUAL(pb->x, pd->x);
	BOOST_CHECK_EQUAL(pb,pd);
	delete pd;
	BMOCK_VERIFY;
}

BOOST_AUTO_TEST_CASE(test_invalid_conversion_downcasting2) {
	__BMOCK_CONTROL(c);
	const bmock::control_tools_ptr t(new bmock::control_tools_mock(c));
	const bmock::location          loc(__FILE__,__LINE__+6);

	__BMOCK_EXPECT_THROW_C(c,new bmock::location(loc),t->error(bmock::message(bmock::BAD_RETURN_TYPE),loc));
	__BMOCK_REPLAY_C(c);
	{
		bmock::test_mode_control c1(bmock::location(__FILE__,__LINE__),t);__BMOCK_CREATE_MOCK_C((&c1),*);
		BOOST_CHECK_THROW(__BMOCK_EXPECT_RETURN_C(c1,base2(),f()),bmock::location);
	}
	__BMOCK_VERIFY_C(c);
}

struct NoDefaultConstructor2
{
	NoDefaultConstructor2(const int x) : x_(x){}
	int x_;
private:	
	NoDefaultConstructor2() {}
};

static NoDefaultConstructor2 g5()
{ 
	MOCK(0, ());
	
	return NoDefaultConstructor2(0); //avoid warning
}

BMOCK_TEST(tester2, test_struct_without_default_constructor)
{
	BMOCK_CREATE_MOCK(g5());
	NoDefaultConstructor2 ndc_expected(5);
	
	BMOCK_EXPECT_RETURN(ndc_expected, g5());
	BMOCK_REPLAY;
	NoDefaultConstructor2 ndc_actual = g5();
	BMOCK_VERIFY;
	BOOST_CHECK_EQUAL(ndc_expected.x_, ndc_actual.x_);
}

struct NoCopyConstructor2
{
	NoCopyConstructor2() : x_(5){}
	int x_;
private:	
	NoCopyConstructor2(const NoCopyConstructor2&) {}
};

static NoCopyConstructor2 ncc_expected;

static NoCopyConstructor2& g6()
{ 
	MOCK(0, ());
	
	return ncc_expected;	//avoid warning
}

BMOCK_TEST(tester2, test_struct_without_copy_constructor)
{
	BMOCK_CREATE_MOCK(g6());	
	
	BMOCK_EXPECT_RETURN(ncc_expected, g6());
	BMOCK_REPLAY;
	NoCopyConstructor2& ncc_actual = g6();
	BMOCK_VERIFY;
	BOOST_CHECK_EQUAL(ncc_expected.x_, ncc_actual.x_);
	BOOST_CHECK_EQUAL(&ncc_expected, &ncc_actual);
}
